package com.ikingtech.platform.service.system.dict.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ikingtech.framework.sdk.base.model.BatchParam;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.dict.api.DictItemApi;
import com.ikingtech.framework.sdk.dict.model.DictItemDTO;
import com.ikingtech.framework.sdk.dict.model.DictItemQueryParamDTO;
import com.ikingtech.framework.sdk.enums.system.dictionary.DictTypeEnum;
import com.ikingtech.framework.sdk.log.embedded.annotation.OperationLog;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.dict.entity.DictDO;
import com.ikingtech.platform.service.system.dict.entity.DictItemDO;
import com.ikingtech.platform.service.system.dict.exception.DictExceptionInfo;
import com.ikingtech.platform.service.system.dict.service.repository.DictItemRepository;
import com.ikingtech.platform.service.system.dict.service.repository.DictRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 系统管理-字典项管理
 * <p>1. 字典项需按租户隔离
 * <p>2. 字典项支持父子级关系
 *
 * @author tie yan
 */
@RequiredArgsConstructor
@ApiController(value = "/system/dict/item", name = "系统管理-字典项管理", description = "系统管理-字典项管理")
public class DictItemController implements DictItemApi {

    private final DictItemRepository repo;

    private final DictRepository dictRepo;

    @Override
    @OperationLog(value = "新增字典项", dataId = "#_res.getData()")
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(DictItemDTO item) {
        DictDO dictEntity = this.dictRepo.getOne(Wrappers.<DictDO>lambdaQuery()
                .eq(DictDO::getId, item.getDictId())
                .and(Tools.Str.isBlank(Me.tenantCode()), wrapper -> wrapper.eq(DictDO::getTenantCode, Tools.Str.EMPTY).or().isNull(DictDO::getTenantCode))
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), DictDO::getTenantCode, Me.tenantCode()));
        if (this.repo.exists(Wrappers.<DictItemDO>lambdaQuery()
                .eq(DictItemDO::getDictCode, item.getDictCode())
                .eq(DictItemDO::getLabel, item.getLabel())
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), DictItemDO::getTenantCode, Me.tenantCode()))) {
            throw new FrameworkException(DictExceptionInfo.DUPLICATE_DICT_ITEM_NAME);
        }

        DictItemDO entity = Tools.Bean.copy(item, DictItemDO.class);
        entity.setId(Tools.Id.uuid());
        entity.setTenantCode(Me.tenantCode());
        entity.setDictCode(dictEntity.getCode());
        entity.setFullPath(this.repo.parseFullPath(item.getParentId(), entity.getId()));
        entity.setPreset(false);
        entity.setSortOrder(this.getMaxSortOrder(item.getParentId()) + 1);
        this.repo.save(entity);
        return R.ok(entity.getId());
    }

    @Override
    @OperationLog(value = "删除字典项")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        DictItemDO entity = this.repo.getById(id);
        if (null == entity) {
            return R.ok();
        }
        String dictType = this.dictRepo.getObj(Wrappers.<DictDO>lambdaQuery()
                .select(DictDO::getType)
                .eq(DictDO::getId, id), String.class::cast);
        if (DictTypeEnum.SYSTEM.name().equals(dictType)) {
            throw new FrameworkException(DictExceptionInfo.DELETE_SYSTEM_DICT_ITEM_IS_NOT_ALLOWED);
        }
        this.repo.remove(Wrappers.<DictItemDO>query().lambda()
                .likeRight(DictItemDO::getFullPath, entity.getFullPath())
                .eq(DictItemDO::getTenantCode, Me.tenantCode()));
        return R.ok();
    }

    @Override
    @OperationLog(value = "更新字典项")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(DictItemDTO item) {
        DictItemDO entity = this.repo.getById(item.getId());
        if (null == entity) {
            throw new FrameworkException(DictExceptionInfo.DICT_ITEM_NOT_FOUND);
        }
        if (this.repo.exists(Wrappers.<DictItemDO>lambdaQuery()
                .ne(DictItemDO::getId, item.getId())
                .eq(DictItemDO::getDictCode, item.getDictCode())
                .eq(DictItemDO::getLabel, item.getLabel())
                .and(Tools.Str.isBlank(Me.tenantCode()), wrapper -> wrapper.eq(DictItemDO::getTenantCode, Tools.Str.EMPTY).or().isNull(DictItemDO::getTenantCode))
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), DictItemDO::getTenantCode, Me.tenantCode()))) {
            throw new FrameworkException(DictExceptionInfo.DUPLICATE_DICT_ITEM_NAME);
        }
        DictItemDO newEntity = Tools.Bean.copy(item, DictItemDO.class);
        newEntity.setFullPath(this.repo.parseFullPath(item.getParentId(), item.getId()));
        entity.setPreset(false);
        this.repo.updateById(newEntity);
        if (!newEntity.getPreset().equals(item.getPreset())) {
            this.repo.update(Wrappers.<DictItemDO>lambdaUpdate()
                    .set(DictItemDO::getPreset, newEntity.getPreset())
                    .likeRight(DictItemDO::getFullPath, newEntity.getFullPath())
                    .eq(DictItemDO::getTenantCode, Me.tenantCode()));
        }
        return R.ok();
    }

    @Override
    public R<List<DictItemDTO>> page(DictItemQueryParamDTO queryParam) {
        return R.ok(PageResult.build(this.repo.page(new Page<>(queryParam.getPage(), queryParam.getRows()),
                        DictItemRepository.createWrapper(queryParam, Me.tenantCode())))
                .convert(entity -> Tools.Bean.copy(entity, DictItemDTO.class)));
    }

    @Override
    public R<List<DictItemDTO>> all() {
        return R.ok(Tools.Coll.convertList(this.repo.list(Wrappers.<DictItemDO>lambdaQuery()
                        .eq(Tools.Str.isNotBlank(Me.tenantCode()), DictItemDO::getTenantCode, Me.tenantCode())
                        .and(Tools.Str.isBlank(Me.tenantCode()), wrapper -> wrapper.eq(DictItemDO::getTenantCode, Tools.Str.EMPTY).or().isNull(DictItemDO::getTenantCode))
                        .orderByDesc(DictItemDO::getCreateTime)),
                entity -> Tools.Bean.copy(entity, DictItemDTO.class)));
    }

    @Override
    public R<DictItemDTO> detail(String id) {
        DictItemDO entity = this.repo.getById(id);
        if (null == entity) {
            throw new FrameworkException(DictExceptionInfo.DICT_ITEM_NOT_FOUND);
        }
        return R.ok(Tools.Bean.copy(entity, DictItemDTO.class));
    }

    @Override
    public R<Map<String, String>> mapByDictName(String dictName) {
        DictDO dictEntity = this.dictRepo.getOne(Wrappers.<DictDO>lambdaQuery()
                .eq(DictDO::getName, dictName).last("limit 1"));
        if (null == dictEntity) {
            return R.ok(Collections.emptyMap());
        }
        return R.ok(Tools.Coll.convertMap(
                this.listByDictCode(dictEntity.getCode(), dictEntity.getTenantCode()),
                DictItemDO::getValue,
                DictItemDO::getLabel
        ));
    }

    @Override
    public R<List<DictItemDTO>> listByDictId(String dictId) {
        DictDO dictEntity = this.dictRepo.getById(dictId);
        if (null == dictEntity) {
            return R.ok(Collections.emptyList());
        }
        return R.ok(Tools.Coll.convertList(this.listByDictCode(dictEntity.getCode(), dictEntity.getTenantCode()),
                entity -> Tools.Bean.copy(entity, DictItemDTO.class)));
    }

    @Override
    public R<List<DictItemDTO>> listByDictCodes(BatchParam<String> dictCodes) {
        if (Tools.Coll.isBlank(dictCodes.getList())) {
            return R.ok(new ArrayList<>());
        }
        return R.ok(Tools.Coll.convertList(this.repo.list(Wrappers.<DictItemDO>lambdaQuery()
                .in(DictItemDO::getDictCode, dictCodes.getList())
                .eq(DictItemDO::getTenantCode, Me.tenantCode())
                .orderByAsc(DictItemDO::getSortOrder)), entity -> Tools.Bean.copy(entity, DictItemDTO.class)));
    }

    private Integer getMaxSortOrder(String parentId) {
        List<Number> orders = this.repo.listObjs(Wrappers.<DictItemDO>query().lambda()
                .select(DictItemDO::getSortOrder)
                .eq(DictItemDO::getParentId, parentId)
                .eq(DictItemDO::getTenantCode, Me.tenantCode())
                .orderByDesc(DictItemDO::getSortOrder));
        return Tools.Coll.isBlank(orders) ? 0 : orders.get(0).intValue();
    }

    private List<DictItemDO> listByDictCode(String dictCode, String tenantCode) {
        return this.repo.list(Wrappers.<DictItemDO>lambdaQuery()
                .eq(DictItemDO::getDictCode, dictCode)
                .and(Tools.Str.isBlank(tenantCode), wrapper -> wrapper.eq(DictItemDO::getTenantCode, Tools.Str.EMPTY).or().isNull(DictItemDO::getTenantCode))
                .eq(Tools.Str.isNotBlank(tenantCode), DictItemDO::getTenantCode, tenantCode)
                .orderByAsc(DictItemDO::getSortOrder));
    }
}
